package com.alibaba.jstorm.container.cgroup.core;

import java.io.IOException;
import java.util.List;

import com.alibaba.jstorm.container.CgroupUtils;
import com.alibaba.jstorm.container.Constants;
import com.alibaba.jstorm.container.SubSystemType;
import com.alibaba.jstorm.container.cgroup.Device;

public class DevicesCore implements CgroupCore {

	private final String dir;

	public static final String DEVICES_ALLOW = "/devices.allow";
	public static final String DEVICES_DENY = "/devices.deny";
	public static final String DEVICES_LIST = "/devices.list";

	public static final char TYPE_ALL = 'a';
	public static final char TYPE_BLOCK = 'b';
	public static final char TYPE_CHAR = 'c';

	public static final int ACCESS_READ = 1;
	public static final int ACCESS_WRITE = 2;
	public static final int ACCESS_CREATE = 4;

	public static final char ACCESS_READ_CH = 'r';
	public static final char ACCESS_WRITE_CH = 'w';
	public static final char ACCESS_CREATE_CH = 'm';

	public DevicesCore(String dir) {
		this.dir = dir;
	}

	@Override
	public SubSystemType getType() {
		// TODO Auto-generated method stub
		return SubSystemType.devices;
	}

	public static class Record {
		Device device;
		char type;
		int accesses;

		public Record(char type, Device device, int accesses) {
			this.type = type;
			this.device = device;
			this.accesses = accesses;
		}

		public Record(String output) {
			if (output.contains("*")) {
				System.out.println("Pre:" + output);
				output = output.replaceAll("\\*", "-1");
				System.out.println("After:" + output);
			}
			String[] splits = output.split("[: ]");
			type = splits[0].charAt(0);
			int major = Integer.parseInt(splits[1]);
			int minor = Integer.parseInt(splits[2]);
			device = new Device(major, minor);
			accesses = 0;
			for (char c : splits[3].toCharArray()) {
				if (c == ACCESS_READ_CH) {
					accesses |= ACCESS_READ;
				}
				if (c == ACCESS_CREATE_CH) {
					accesses |= ACCESS_CREATE;
				}
				if (c == ACCESS_WRITE_CH) {
					accesses |= ACCESS_WRITE;
				}
			}
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append(type);
			sb.append(' ');
			sb.append(device.major);
			sb.append(':');
			sb.append(device.minor);
			sb.append(' ');
			sb.append(getAccessesFlag(accesses));

			return sb.toString();
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + accesses;
			result = prime * result
					+ ((device == null) ? 0 : device.hashCode());
			result = prime * result + type;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Record other = (Record) obj;
			if (accesses != other.accesses)
				return false;
			if (device == null) {
				if (other.device != null)
					return false;
			} else if (!device.equals(other.device))
				return false;
			if (type != other.type)
				return false;
			return true;
		}

		public static Record[] parseRecordList(List<String> output) {
			Record[] records = new Record[output.size()];
			for (int i = 0, l = output.size(); i < l; i++) {
				records[i] = new Record(output.get(i));
			}

			return records;
		}

		public static StringBuilder getAccessesFlag(int accesses) {
			StringBuilder sb = new StringBuilder();
			if ((accesses & ACCESS_READ) != 0) {
				sb.append(ACCESS_READ_CH);
			}
			if ((accesses & ACCESS_WRITE) != 0) {
				sb.append(ACCESS_WRITE_CH);
			}
			if ((accesses & ACCESS_CREATE) != 0) {
				sb.append(ACCESS_CREATE_CH);
			}
			return sb;
		}
	}

	private void setPermission(String prop, char type, Device device,
			int accesses) throws IOException {
		Record record = new Record(type, device, accesses);
		CgroupUtils.writeFileByLine(Constants.getDir(this.dir, prop),
				record.toString());
	}

	public void setAllow(char type, Device device, int accesses)
			throws IOException {
		setPermission(DEVICES_ALLOW, type, device, accesses);
	}

	public void setDeny(char type, Device device, int accesses)
			throws IOException {
		setPermission(DEVICES_DENY, type, device, accesses);
	}

	public Record[] getList() throws IOException {
		List<String> output = CgroupUtils.readFileByLine(Constants.getDir(
				this.dir, DEVICES_LIST));
		return Record.parseRecordList(output);
	}

}
